/*
 * xds.c
 *
 */

/// realize interfaces
#include "xds.h"

/// require interfaces
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "cpuload.h"
#include "SEGGER_RTT.h"

/// define macros
#define	XDS_RTT_BUFFER_SIZE		8
#define	XDS_REQ_BUFFER_SIZE		16
#define	XDS_REQ_ARGUMENT_MAX	4
#define	XDS_DFT_PRESCALER		50		/// 50*20=1000 sec
#define	XDS_DFT_DISABLE			250		/// 5 sec
#define	XDS_PRT_DISABLE			250		/// 5 sec

/// define data struct
typedef struct
{
	struct{
		uint16_t timeMs;
		uint16_t timeS;
		uint8_t count;
	}Timer;
	struct{
		uint8_t enable[XDS_USER_TOTAL_NUMBERS];	/// info, warn, err, always
		uint8_t	level;
	}Printf;
	struct{
		uint8_t buffer[XDS_RTT_BUFFER_SIZE];
		uint8_t length;
	}RttBuffer;
	struct{
		char	buffer[XDS_REQ_BUFFER_SIZE];
		uint8_t length;
		bool ready;
		int argc;
		char * argv[XDS_REQ_ARGUMENT_MAX];
	}ReqBuffer;
}
XDS_Data_T;

XDS_Data_T	xds_Data;

const char * xds_UserNames[XDS_USER_TOTAL_NUMBERS] = {XDS_USER_NAMES};
const uint8_t xds_DefaultEnable[XDS_USER_TOTAL_NUMBERS] = {XDS_DEFAULT_LEVELS};

static void xds_ReceiveRequest(void);
static void xds_ProcessRequest(void);
static void xds_Main(int argc, char** argv);
static void xds_list(void);
static void xds_info(void);

void xds_Init(void)
{
	xds_Data.ReqBuffer.length = 0;
	for(uint8_t i=0;i<XDS_USER_TOTAL_NUMBERS;i++) xds_Data.Printf.enable[i] = xds_DefaultEnable[i];	/// always + debug + error
	xds_Data.Printf.level = 0;

	/// print out the build date time
	xds_printf(XDS_USER_XDS, XDS_LEVEL_ALWAYS, "\r\nbuild on %s, %s\r\n", __DATE__, __TIME__);
	xds_list();
	xds_info();
}

void xds_Task1ms25(void)
{
	xds_Data.Timer.timeMs++;
	xds_Data.Timer.count++;
	xds_Data.Timer.count &= 0x03;
	if(xds_Data.Timer.count<=0) xds_Data.Timer.timeMs++;
	if(xds_Data.Timer.timeMs>=1000){
		xds_Data.Timer.timeMs-=1000;
		xds_Data.Timer.timeS++;
	}
}

void xds_Task20ms(void)
{
	/// receive request
	xds_ReceiveRequest();

	/// process request
	xds_ProcessRequest();
}

int xds_printf(XDS_USER user, XDS_LEVEL level, const char * sFormat, ...)
{
	int r = 0;
	if(user<XDS_USER_TOTAL_NUMBERS && level<XDS_LEVEL_TOTAL_NUMBERS)
	{
		uint8_t mask = (1<<level);
		if(xds_Data.Printf.enable[user] & mask)
		{
			const char level2letter[XDS_LEVEL_TOTAL_NUMBERS] = {'A','D','E','W','I'};
			SEGGER_RTT_printf(0, "%d.%03d %d%% [%d-%s-%c] ",
					xds_Data.Timer.timeS, xds_Data.Timer.timeMs,
					Cpuload_GetCpuloadPct(),
					user, xds_UserNames[user], level2letter[level]);
			va_list ParamList;
			va_start(ParamList, sFormat);
			r = SEGGER_RTT_vprintf(0, sFormat, &ParamList);
			va_end(ParamList);
		}
	}
	return r;
}

int xds_printfNoHeader(XDS_USER user, XDS_LEVEL level, const char * sFormat, ...)
{
	int r = 0;
	if(user<XDS_USER_TOTAL_NUMBERS && level<XDS_LEVEL_TOTAL_NUMBERS)
	{
		uint8_t mask = (1<<level);
		if(xds_Data.Printf.enable[user] & mask)
		{
			va_list ParamList;
			va_start(ParamList, sFormat);
			r = SEGGER_RTT_vprintf(0, sFormat, &ParamList);
			va_end(ParamList);
		}
	}
	return r;
}

static void xds_ReceiveRequest(void)
{
	/// read inputs from RTT buffer
	xds_Data.RttBuffer.length = SEGGER_RTT_Read(0, xds_Data.RttBuffer.buffer, XDS_RTT_BUFFER_SIZE);

	/// put data from RTT buffer to the request buffer
	if(xds_Data.ReqBuffer.length + xds_Data.RttBuffer.length < XDS_REQ_BUFFER_SIZE)
	{
		/// copy to the buffer
		for(uint8_t i=0;i<xds_Data.RttBuffer.length;i++)
		{
			xds_Data.ReqBuffer.buffer[xds_Data.ReqBuffer.length] = xds_Data.RttBuffer.buffer[i];
			xds_Data.ReqBuffer.length++;
		}
	}
	else
	{
		/// request buffer overflow, clear the request buffer
		xds_Data.ReqBuffer.length = 0;
	}

	/// request end
	if(xds_Data.ReqBuffer.length>0 && xds_Data.ReqBuffer.buffer[xds_Data.ReqBuffer.length-1]==10)
	{
		/// end the request
		xds_Data.ReqBuffer.length--;
		xds_Data.ReqBuffer.buffer[xds_Data.ReqBuffer.length] = 0;
		/// request is ready
		xds_Data.ReqBuffer.ready = true;
		/// clear the request
		xds_Data.ReqBuffer.length = 0;
	}
}

static void xds_list(void)
{
	uint8_t i;
	SEGGER_RTT_printf(0, "XDS LIST\r\n");
	SEGGER_RTT_printf(0, "- xds: XDS control\r\n");
	for(i=0;i<XDS_ITEM_TOTAL_NUMBERS;i++)
	{
		SEGGER_RTT_printf(0, "- %s: %s\r\n", Xds_ItemDesc[i].key, Xds_ItemDesc[i].desc);
	}
}

static void xds_ProcessRequest(void)
{
	if(xds_Data.ReqBuffer.ready)
	{
		const char seps[] = " ";
		char * token;

		/// clear ready flag
		xds_Data.ReqBuffer.ready = false;

		/// show the request
		xds_printf(XDS_USER_XDS, XDS_LEVEL_INFO, "process request (%d): %s\r\n", xds_Data.ReqBuffer.length, xds_Data.ReqBuffer.buffer);

		/// analyze arguments
		xds_Data.ReqBuffer.argc = 0;
		token = strtok(xds_Data.ReqBuffer.buffer, seps);
		while(token!=NULL)
		{
			if(xds_Data.ReqBuffer.argc<XDS_REQ_ARGUMENT_MAX)
			{
				xds_Data.ReqBuffer.argv[xds_Data.ReqBuffer.argc] = token;
				xds_Data.ReqBuffer.argc++;
			}
			else
			{
				xds_printf(XDS_USER_XDS, XDS_LEVEL_ERROR, "Too Many Arguments: >%d\r\n", xds_Data.ReqBuffer.argc);
				xds_Data.ReqBuffer.argc = 0;
				token = NULL;
				break;
			}
			/// next
			token = strtok(NULL, seps);
		}

		/// show argc, argv
		if(xds_Data.ReqBuffer.argc>0)
		{
			xds_printf(XDS_USER_XDS, XDS_LEVEL_ALWAYS, "main(%d, {%s", xds_Data.ReqBuffer.argc, xds_Data.ReqBuffer.argv[0]);
			for(int i=1;i<xds_Data.ReqBuffer.argc;i++) xds_printfNoHeader(XDS_USER_XDS, XDS_LEVEL_ALWAYS, ", %s", xds_Data.ReqBuffer.argv[i]);
			xds_printfNoHeader(XDS_USER_XDS, XDS_LEVEL_ALWAYS, "})\r\n");
		}

		/// process the request
		if(xds_Data.ReqBuffer.argc>0)
		{
			if(strcmp(xds_Data.ReqBuffer.argv[0], "xds")==0)
			{
				xds_Main(xds_Data.ReqBuffer.argc, xds_Data.ReqBuffer.argv);
			}
			else
			{
				/// find the key
				for(int i=0;i<XDS_ITEM_TOTAL_NUMBERS;i++)
				{
					if(strcmp(Xds_ItemDesc[i].key,xds_Data.ReqBuffer.argv[0])==0)
					{
						/// execute the function
						Xds_MainFunc_T func = Xds_ItemDesc[i].main;
						if(func!=NULL)
						{
							func(xds_Data.ReqBuffer.argc, xds_Data.ReqBuffer.argv);
							/// done
							return;
						}
					}
				}
				/// find nothing
				xds_list();
			}
		}
		else
		{
			xds_list();
		}
	}
}

static void xds_info(void)
{
	uint8_t i;
	SEGGER_RTT_printf(0, "XDS INFO (bit4-0: info, warning, error, debug, always):\n");
	for(i=0;i<XDS_USER_TOTAL_NUMBERS;i++)
	{
		SEGGER_RTT_printf(0, "- %d: %s, enable=%Xh\n", i, xds_UserNames[i], xds_Data.Printf.enable[i]);
	}
}

static void xds_help(void)
{
	SEGGER_RTT_printf(0, "XDS HELP\r\n");
	SEGGER_RTT_printf(0, "- xds help: show this menu\r\n");
	SEGGER_RTT_printf(0, "- xds info: show current xds enables\r\n");
	SEGGER_RTT_printf(0, "- xds ena [user] [enable]: set enable of the user\r\n");
}

static void xds_Main(int argc, char** argv)
{
	if(argc==2)
	{
		if(strcmp(argv[1],"help")==0)
		{
			xds_help();
		}
		else if(strcmp(argv[1],"info")==0)
		{
			xds_info();
		}
		else
		{
			xds_help();
		}
	}
	else if(argc==4)
	{
		if(strcmp(argv[1],"ena")==0)
		{
			char * pEnd;
			uint32_t user = strtoul(argv[2], &pEnd, 10);
			uint32_t ena = strtoul(argv[3], &pEnd, 10);
			if(user < XDS_USER_TOTAL_NUMBERS)
			{
				xds_Data.Printf.enable[user] = ena;
				xds_info();
			}
			else
			{
				SEGGER_RTT_printf(0, "WRONG user(%d) or enable(%Xh)\r\n", user, ena);
			}
		}
		else
		{
			xds_help();
		}
	}
	else
	{
		xds_help();
	}
}

